// FMemoryManager.h (c) 2011 JV Software
//

#define USING_FMEMORY

//
// FMemoryZone
//
enum FMemoryZone {
	FMEMORY_ZONE_PERSISTANT = 0, // Memory objects that will last through game change.
	FMEMORY_ZONE_GAME,			 // Memory objects that are only persistant during a game.
	FMEMORY_ZONE_TEMPBUFFER
};

//
// FMemoryNode
//
struct FMemoryNode {
	FObject					*ptr;
	byte					zoneType;
	int						nodeNum;
	short					nodeMemorySize;
	void					*nodePtr;
};

//
// FMemoryManager
//
class FMemoryManager {
public:
	ENGINE_API void			Init( void );

	template<class T>
	T						*AllocObject(FMemoryZone zoneType);

	ENGINE_API void			FreeMemoryObject( FObject *ptr );

	ENGINE_API bool			ReserveMemory( int size );

	ENGINE_API void			AllowMemoryModification( void ) { forceAllowMemoryModification = true; };

	ENGINE_API void			DisallowMemoryModification( void ) { forceAllowMemoryModification = false; };

	ENGINE_API int			MemoryUsage( void ) { return memPoolSize; }
private:
	ENGINE_API void			AppendNode( FMemoryNode &node );
protected:
	int	memPoolSize;
	short lastNodeSize;
	bool allowMemoryModification;
	bool forceAllowMemoryModification;
};

/*
===============
FMemoryManager::AllocObject
===============
*/
template<class T>
JINLINE T	*FMemoryManager::AllocObject(FMemoryZone zoneType) {
	FMemoryNode		node;
	
	allowMemoryModification = true;
	node.zoneType = zoneType;

	if(zoneType >= FMEMORY_ZONE_TEMPBUFFER) {
		node.ptr = (FObject *)new T[(int)zoneType];
	}
	else {
		node.ptr = (FObject *)new T();
	}
	
	AppendNode( node );

	node.ptr->nodeNum = node.nodeNum;

	return (T *)node.ptr;
}

extern ENGINE_API FMemoryManager *memory;

// Override new and delete to always error out, always use the memory manager instead!
JINLINE void* operator new (size_t size)
{
	byte *p=(byte *)malloc(size + sizeof( int ) + sizeof( int )); 

	if(!memory->ReserveMemory( size )) {
		_asm { int 3h };
	}

	
	if (p==0) // did malloc succeed?
	  throw std::bad_alloc(); // ANSI/ISO compliant behavior

	(*(int *)p) = size;

	return ((byte *)(p + sizeof( int ) + sizeof( int )));
}

JINLINE void operator delete (void *p)
{
	byte *pAddr = ((byte *)p) - 8;
	if(!memory->ReserveMemory( -*((int *)pAddr) )) {
		_asm { int 3h };
	}

	free(pAddr); 
}

JINLINE void operator delete[] (void *p)
{
	if(p == NULL)
		return;

	delete p; 
}

#define AllocBuffer(T, S) memory->AllocObject<T>((FMemoryZone)S);
#define AllocObject(T) memory->AllocObject<T>(FMEMORY_ZONE_PERSISTANT)
#define AllocGameObject(T) memory->AllocObject<T>(FMEMORY_ZONE_GAME)
#define FreeObject(T) memory->FreeMemoryObject( T )
